<?php

declare(strict_types=1);
/**
 * This file is part of Hyperf.
 *
 * @link     https://www.hyperf.io
 * @document https://hyperf.wiki
 * @contact  group@hyperf.io
 * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
 */

namespace App\Service;

use App\Job\JPushGroupChatJob;
use App\Job\PushGlobalNotificationJob;
use App\Log\Log;
use App\Model\Admin;
use App\Model\ChatTransact;
use App\Model\ChatTransactGroup;
use App\Model\TransactOrder;
use App\Model\User;
use Hyperf\Contract\OnCloseInterface;
use Hyperf\Contract\OnMessageInterface;
use Hyperf\Contract\OnOpenInterface;
use Hyperf\Di\Annotation\Inject;
use Hyperf\Utils\ApplicationContext;
use Hyperf\WebSocketServer\Context;


class WebSocketTransactController implements OnMessageInterface, OnOpenInterface, OnCloseInterface
{
    /**
     * @var QueueService
     */
    #[Inject]
    protected $queueServer;


    public const WS_TYPE_USER = 1; // 卖方

    public const WS_TYPE_CUSTOMER_USER = 2; // 客服

    public const REDIS_USER_KEY = 'Remit_WebSocketTransactController_fd_with_user_id_';

    public const REDIS_CUSTOMER_KEY = 'Remit_WebSocketTransactController_fd_with_customer_id_';

    public const REDIS_USER_FD_KEY = 'Remit_WebSocketTransactController_user_id_with_fd_';

    public const REDIS_CUSTOMER_FD_KEY = 'Remit_WebSocketTransactController_customer_id_with_fd_';


    public const SUCCESS = '0_SUCCESS';

    public const USER_EMPTY = '10005_用户不存在';

    public const TOKEN_ERROR = '10100_token验证失败';

    public const TRANSACT_ORDER_ID_HAVA_ERROR = '10101_transact_order_id订单号id必传';
    public const TRANSACT_ORDER_ID_ERROR = '10102_该订单无权限';

    public function onMessage($server, $frame): void
    {
        // 解析传过来的参数
        $data = json_decode($frame->data, true);

        // 是不是心跳包
        if (isset($data['type']) && $data['type'] == 'Ping') {
            $server->push($frame->fd, 'Ping'); // 心跳
        } else {

            // 判断有没有传订单id
            if (!isset($data['transact_order_id'])) {
                if ($server->exist($frame->fd)) {
                    $server->push($frame->fd, $this->outResponse(self::TRANSACT_ORDER_ID_HAVA_ERROR));
                }
                return;
            }

            // 判断是否创建
            $chatTransact = ChatTransact::where([
                'transact_order_id' => $data['transact_order_id'],
            ])->first();

            if (!$chatTransact) {
                $transactOrder = TransactOrder::find($data['transact_order_id']);

                $chatTransact = ChatTransact::firstOrCreate([
                    'transact_order_id' => $transactOrder->id,
                    'order_no' => $transactOrder->order_no,
                    'pay_uid' => $transactOrder->pay_uid,
                    'sell_uid' => $transactOrder->sell_uid,
                    'transact_order_state' => $transactOrder->state,
                    'custome_id' => 0,
                    'state' => 'close',
                ]);
            }

            // 根据token 获取当前fd 对应的用户信息
            $token = json_decode(Context::get('token'), true);

            $redis = ApplicationContext::getContainer()->get(\Redis::class);

            if ($token['type'] == self::WS_TYPE_USER) {
                // 普通用户
                $params = [
                    'transact_order_id' => $data['transact_order_id'],
                    'type' => $data['type'],
                    'content' => $data['content'],
                    'img_path' => $data['img_path'],
                ];

                if ($token['id'] == $chatTransact->sell_uid) {

                    $params['push_type'] = 'sell_user';
                    $params['sell_uid'] = $token['id'];
                    $params['pay_uid'] = $chatTransact->pay_uid;

                    $chatGroup = ChatTransactGroup::create($params);

                    $chatSource = ChatTransactGroup::with('payUser')->with('sellUser')->with('customer')->find($chatGroup->id);

                    if ($server->exist($frame->fd)) {
                        $server->push($frame->fd, $this->outResponse(self::SUCCESS, $chatSource->toArray()));
                    }

                    $pay_user_id = $redis->get(self::REDIS_USER_KEY . $chatTransact->pay_uid);
                    if ($pay_user_id) {
                        if ($server->exist((int)$pay_user_id)) {
                            $server->push((int)$pay_user_id, $this->outResponse(self::SUCCESS, $chatSource->toArray()));
                        }
                    } else {
                        $chatTransact->increment('pay_unread_num');
                        $this->queueServer->push(JPushGroupChatJob::class, [
                            'order_id' => $chatSource->transact_order_id,
                            'user_id' => $chatSource->pay_uid,
                            'user_type' => 1, // 1买方 2卖方
                        ]);
                    }

                    if ($chatTransact->state == 'open') {
                        $customer_user_fd = $redis->get(self::REDIS_CUSTOMER_KEY . $chatTransact->custome_id);
                        if ($customer_user_fd) {
                            if ($server->exist((int)$customer_user_fd)) {
                                $server->push((int)$customer_user_fd, $this->outResponse(self::SUCCESS, $chatSource->toArray()));
                            }
                        } else {
                            $chatTransact->increment('custom_unread_num');
                            $this->queueServer->push(PushGlobalNotificationJob::class, [
                                'type' => 'transact',
                            ]);
                        }
                    }

                } elseif ($token['id'] == $chatTransact->pay_uid) {

                    $params['push_type'] = 'pay_user';
                    $params['pay_uid'] = $token['id'];
                    $params['sell_uid'] = $chatTransact->sell_uid;

                    $chatGroup = ChatTransactGroup::create($params);

                    $chatSource = ChatTransactGroup::with('payUser')->with('sellUser')->with('customer')->find($chatGroup->id);

                    if ($server->exist($frame->fd)) {
                        $server->push($frame->fd, $this->outResponse(self::SUCCESS, $chatSource->toArray()));
                    }

                    $sell_user_fd = $redis->get(self::REDIS_USER_KEY . $chatTransact->sell_uid);
                    if ($sell_user_fd) {
                        if ($server->exist((int)$sell_user_fd)) {
                            $server->push((int)$sell_user_fd, $this->outResponse(self::SUCCESS, $chatSource->toArray()));
                        }
                    } else {
                        $chatTransact->increment('sell_unread_num');
                        $this->queueServer->push(JPushGroupChatJob::class, [
                            'order_id' => $chatSource->transact_order_id,
                            'user_id' => $chatSource->sell_uid,
                            'user_type' => 2, // 1买方 2卖方
                        ]);
                    }

                    if ($chatTransact->state == 'open') {
                        $customer_user_fd = $redis->get(self::REDIS_CUSTOMER_KEY . $chatTransact->custome_id);
                        if ($customer_user_fd) {
                            if ($server->exist((int)$customer_user_fd)) {
                                $server->push((int)$customer_user_fd, $this->outResponse(self::SUCCESS, $chatSource->toArray()));
                            }
                        } else {
                            $chatTransact->increment('custom_unread_num');
                            $this->queueServer->push(PushGlobalNotificationJob::class, [
                                'type' => 'transact',
                            ]);
                        }
                    }
                } else {

                    Log::info('用户id: ' . $token['id']);
                    Log::info('买方id: ' . $chatTransact->pay_user_id);
                    Log::info('卖方id: ' . $chatTransact->sell_user_id);
                    Log::info('订单id: ' . $data['transact_order_id']);

                    if ($server->exist($frame->fd)) {
                        $server->push($frame->fd, $this->outResponse(self::TRANSACT_ORDER_ID_ERROR));
                    }
                    return;
                }
            }

            if ($token['type'] == self::WS_TYPE_CUSTOMER_USER) {
                $params = [
                    'push_type' => 'custom_user',
                    'type' => $data['type'],
                    'content' => $data['content'],
                    'img_path' => $data['img_path'],
                    'customer_id' => $token['id'],
                    'transact_order_id' => $data['transact_order_id'],
                    'sell_uid' => $chatTransact->sell_uid,
                    'pay_uid' => $chatTransact->pay_uid,
                ];

                $sell_user_fd = $redis->get(self::REDIS_USER_KEY . $chatTransact->sell_uid);
                $pay_user_fd = $redis->get(self::REDIS_USER_KEY . $chatTransact->pay_uid);

                // 判断有没有客服id存不存在
                if ($chatTransact->custome_id == 0 || $chatTransact->custome_id != $token['id']) {

                    $chatTransact->custome_id = $token['id'];
                    $chatTransact->save();

                    $params1 = $params;
                    $params1['content'] = '客服: ' . Admin::find($token['id'])->user_name . '已介入';
                    $chatGroup1 = ChatTransactGroup::create($params1);

                    $chatSource = ChatTransactGroup::with('payUser')->with('sellUser')->with('customer')->find($chatGroup1->id);

                    if ($server->exist($frame->fd)) {
                        $server->push($frame->fd, $this->outResponse(self::SUCCESS, $chatSource->toArray()));
                    }

                    if ($sell_user_fd) {
                        if ($server->exist((int)$sell_user_fd)) {
                            $server->push((int)$sell_user_fd, $this->outResponse(self::SUCCESS, $chatSource->toArray()));
                        }
                    }else{
                        $chatTransact->increment('sell_unread_num');
                    }
                    if ($pay_user_fd) {
                        if ($server->exist((int)$pay_user_fd)) {
                            $server->push((int)$pay_user_fd, $this->outResponse(self::SUCCESS, $chatSource->toArray()));
                        }
                    }else{
                        $chatTransact->increment('pay_unread_num');
                    }
                }

                $chatGroup2 = ChatTransactGroup::create($params);
                $chatSource2 = ChatTransactGroup::with('payUser')->with('sellUser')->with('customer')->find($chatGroup2->id);

                if ($server->exist($frame->fd)) {
                    $server->push($frame->fd, $this->outResponse(self::SUCCESS, $chatSource2->toArray()));
                }

                if ($sell_user_fd) {
                    if ($server->exist((int)$sell_user_fd)) {
                        $server->push((int)$sell_user_fd, $this->outResponse(self::SUCCESS, $chatSource2->toArray()));
                    }
                } else {
                    $chatTransact->increment('sell_unread_num');
                    $this->queueServer->push(JPushGroupChatJob::class, [
                        'order_id' => $chatSource2->transact_order_id,
                        'user_id' => $chatSource2->sell_uid,
                        'user_type' => 2, // 1买方 2卖方
                    ]);
                }

                if ($pay_user_fd) {
                    if ($server->exist((int)$pay_user_fd)) {
                        $server->push((int)$pay_user_fd, $this->outResponse(self::SUCCESS, $chatSource2->toArray()));
                    }
                } else {
                    $chatTransact->increment('pay_unread_num');
                    $this->queueServer->push(JPushGroupChatJob::class, [
                        'order_id' => $chatSource2->transact_order_id,
                        'user_id' => $chatSource2->pay_uid,
                        'user_type' => 1, // 1买方 2卖方
                    ]);
                }
            }
        }
        /*
        if($frame->opcode == Opcode::PING) {
            // 如果使用协程 Server，在判断是 PING 帧后，需要手动处理，返回 PONG 帧。
            // 异步风格 Server，可以直接通过 Swoole 配置处理，详情请见 https://wiki.swoole.com/#/websocket_server?id=open_websocket_ping_frame
            $server->push('', Opcode::PONG);
            return;
        }
        $server->push($frame->fd, 'Recv: ' . $frame->data);
        */
    }

    public function onClose($server, int $fd, int $reactorId): void
    {

        // 根据fd 找出用户缓存删除
        $user_id = ApplicationContext::getContainer()->get(\Redis::class)->get(self::REDIS_USER_FD_KEY . $fd);

        // 根据fd 找出客服缓存删除
        $customer_id = ApplicationContext::getContainer()->get(\Redis::class)->get(self::REDIS_CUSTOMER_FD_KEY . $fd);

        if ($user_id) {
            ApplicationContext::getContainer()->get(\Redis::class)->del(
                self::REDIS_USER_KEY . $user_id,
                self::REDIS_USER_FD_KEY . $fd
            );
        }

        if ($customer_id) {
            ApplicationContext::getContainer()->get(\Redis::class)->del(
                self::REDIS_CUSTOMER_KEY . $customer_id,
                self::REDIS_CUSTOMER_FD_KEY . $fd
            );
        }
    }

    public function onOpen($server, $request): void
    {
        if ($request->get == null) {
            $server->push($request->fd, $this->outResponse(self::TOKEN_ERROR));
            $server->close($request->fd);
        }

        if (!is_array($request->get)) {
            $server->push($request->fd, $this->outResponse(self::TOKEN_ERROR));
            $server->close($request->fd);
        }

        if (!isset($request->get['token'])) {
            $server->push($request->fd, $this->outResponse(self::TOKEN_ERROR));
            $server->close($request->fd);
        }

        $isValid = $this->updateUserOnlineState($server, $request);

        if (!$isValid) {
            $server->close($request->fd);
        }

        // 设置上下文
        Context::set('token', base64_decode($request->get['token']));
        // $server->push($request->fd, 'Opened');
    }

    /**
     * 聊天服务统一输出.
     *
     * $message
     * $data
     * @param mixed $message
     * @param mixed $data
     */
    public function outResponse($message, $data = [])
    {
        $code = strstr($message, '_', true);
        $msg = str_replace('_', '', strstr($message, '_'));

        $responseData = [
            'code' => $code,
            'msg' => $msg,
            'data' => $data,
        ];

        return json_encode($responseData);
    }

    public function updateUserOnlineState($server, $request): bool
    {
        $data = json_decode(base64_decode($request->get['token']), true);
        if (!is_array($data)) {
            $server->push($request->fd, $this->outResponse(self::TOKEN_ERROR));
            return false;
        }

        if (!isset($data['type'])) {
            $server->push($request->fd, $this->outResponse(self::TOKEN_ERROR));
            return false;
        }
        if (!isset($data['id'])) {
            $server->push($request->fd, $this->outResponse(self::TOKEN_ERROR));
            return false;
        }

        // 普通用户
        if ($data['type'] == self::WS_TYPE_USER) {
            $user = User::find($data['id']);
            if (!$user) {
                $server->push($request->fd, $this->outResponse(self::USER_EMPTY));
                return false;
            }

            // 记录fd
            ApplicationContext::getContainer()->get(\Redis::class)->set(self::REDIS_USER_KEY . $user->id, $request->fd, 60 * 60 * 10);

            ApplicationContext::getContainer()->get(\Redis::class)->set(self::REDIS_USER_FD_KEY . $request->fd, $user->id, 60 * 60 * 10);
        }

        // 客服用户
        if ($data['type'] == self::WS_TYPE_CUSTOMER_USER) {
            $admin = Admin::find($data['id']);
            if (!$admin) {
                $server->push($request->fd, $this->outResponse(self::USER_EMPTY));
                return false;
            }

            // 记录fd
            ApplicationContext::getContainer()->get(\Redis::class)->set(self::REDIS_CUSTOMER_KEY . $admin->id, $request->fd, 60 * 60 * 10);

            ApplicationContext::getContainer()->get(\Redis::class)->set(self::REDIS_CUSTOMER_FD_KEY . $request->fd, $admin->id, 60 * 60 * 10);
        }

        return true;
    }
}
